# LeetCode 416、分割等和子集

# 一、题目描述

给你一个 只包含正整数非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

# 二、题目解析

# 三、参考代码

# 1、Java 代码

class Solution {
    public boolean canPartition(int[] nums) {

        // 使用 sum 来保存数组中全部元素的和
        int sum = 0;

        // 遍历 nums 数组
        for (int i = 0 ; i < nums.length ; i++){
            // 把 nums 数组上的所有元素值累加到 sum 上
            sum += nums[i];
        }

        // 如果发现 sum 为奇数,那么必然无法拆分为两个相等的整数
        if(sum % 2 == 1){
            // 所以无法将这个数组分割成两个子集,返回false
            return false;
        }

        // 获取数组的长度
        int n = nums.length;

        // 获取数组中全部元素之后的一半
        // 接下来需要在数组 nums 中寻找一些数字去拼凑为 target
        // 如果能找到一些数字之和为 target,那么剩下的数字之和也是 target
        int target = sum / 2;

        // dp[i][j] 表示 nums 的前 i 个元素能否可以组成和为 j 的结果
        // dp[0][0] 表示 nums 的前 0 个元素能否可以组成和为 0 的结果
        // dp[2][6] 表示 nums 的前 2 个元素能否可以组成和为 6 的结果
        // dp[n - 1][target ] 表示 nums 的前 n - 1 个元素能否可以组成和为 target 的结果
        // i 的值从 0 到 n - 1
        // j 的值从 0 到 target
        boolean[][] dp = new boolean[n][target + 1];

        // 先初始化  dp[0][nums[0]]
        // 如果 nums 的第 0 个元素 nums[0] 小于 target
        if (nums[0] <= target) {
            // 那么 nums 的前 0 个元素能否可以组成和为 nums[0] 的结果是 true
            // 因为 nums 的前 0 个元素就是 nums[0]
            dp[0][nums[0]] = true;
        }
        

        // 接下来开始往 dp 数组中填充结果
        // i 的值从 1 到 n - 1
        for (int i = 1; i < n; i++) {
            // j 的值从 0 到 target
            for (int j = 0; j <= target; j++) {
                // dp[i][j] 表示 nums 的前 i 个元素能否可以组成和为 j 的结果
                // dp[i - 1][j] 表示 nums 的前 i - 1 个元素能否可以组成和为 j 的结果
                // 对于 dp[i][j] 来说,如果 dp[i - 1][j] 为 true,那么 dp[i][j] 必然也为 true
                // 所以,先初始化 dp[i][j] 的值为 dp[i - 1][j] 的值
                // 再通过后面的代码修改 dp[i][j] 中还为 false 的值 
                dp[i][j] = dp[i - 1][j];

                // 如果此时 nums[i] 恰巧为 j
                // 那么对于 dp[i][j] 来说,nums 的前 i 个元素可以组成和为 j 的结果
                if (nums[i] == j) {
                    // 所以 dp[i][j] 为 true
                    dp[i][j] = true;
                    // 同时继续
                    continue;
                }

                // 如果发现 nums[i] 小于 j
                if (nums[i] < j) {
                    // dp[i][j] 表示 nums 的前 i 个元素能否可以组成和为 j 的结果
                    // dp[i - 1][j] 表示 nums 的前 i - 1 个元素能否可以组成和为 j 的结果
                    // dp[i - 1][j - nums[i]] 表示 nums 的前 i - 1 个元素能否可以组成和为 j - nums[i] 的结果
                    // 那么 dp[i][j] 的结果要为 true
                    // 1、nums 的前 i - 1 个元素可以组成和为 j 
                    // 2、nums 的前 i - 1 个元素可以组成和为 j - nums[i]
                    // 对于 1 来说,不用使用 nums[i] 就组成了 j
                    // 对于 2 来说,前 i - 1 个元素可以组成和为 j - nums[i],那么加上此时的值 nums[i],也组成了 j
                    dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i]];
                }
            }
        }

        // dp[n - 1][target ] 表示 nums 的前 n - 1 个元素能否可以组成和为 target 的结果
        // 返回这个结果
        return dp[n - 1][target];

    }
}

# **2、**C++ 代码

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        // 使用 sum 来保存数组中全部元素的和
        int sum = 0;

        // 遍历 nums 数组
        for (int i = 0 ; i < nums.size() ; i++){
            // 把 nums 数组上的所有元素值累加到 sum 上
            sum += nums[i];
        }

        // 如果发现 sum 为奇数,那么必然无法拆分为两个相等的整数
        if(sum % 2 == 1){
            // 所以无法将这个数组分割成两个子集,返回false
            return false;
        }

        // 获取数组的长度
        int n = nums.size();

        // 获取数组中全部元素之后的一半
        // 接下来需要在数组 nums 中寻找一些数字去拼凑为 target
        // 如果能找到一些数字之和为 target,那么剩下的数字之和也是 target
        int target = sum / 2;

        // dp[i][j] 表示 nums 的前 i 个元素能否可以组成和为 j 的结果
        // dp[0][0] 表示 nums 的前 0 个元素能否可以组成和为 0 的结果
        // dp[2][6] 表示 nums 的前 2 个元素能否可以组成和为 6 的结果
        // dp[n - 1][target ] 表示 nums 的前 n - 1 个元素能否可以组成和为 target 的结果
        // i 的值从 0 到 n - 1
        // j 的值从 0 到 target
        auto dp = vector < vector < bool>> ( n ,vector<bool> (target + 1));

        // 先初始化  dp[0][nums[0]]
        // 如果 nums 的第 0 个元素 nums[0] 小于 target
        if (nums[0] <= target) {
            // 那么 nums 的前 0 个元素能否可以组成和为 nums[0] 的结果是 true
            // 因为 nums 的前 0 个元素就是 nums[0]
            dp[0][nums[0]] = true;
        }
        

        // 接下来开始往 dp 数组中填充结果
        // i 的值从 1 到 n - 1
        for (int i = 1; i < n; i++) {
            // j 的值从 0 到 target
            for (int j = 0; j <= target; j++) {
                // dp[i][j] 表示 nums 的前 i 个元素能否可以组成和为 j 的结果
                // dp[i - 1][j] 表示 nums 的前 i - 1 个元素能否可以组成和为 j 的结果
                // 对于 dp[i][j] 来说,如果 dp[i - 1][j] 为 true,那么 dp[i][j] 必然也为 true
                // 所以,先初始化 dp[i][j] 的值为 dp[i - 1][j] 的值
                // 再通过后面的代码修改 dp[i][j] 中还为 false 的值 
                dp[i][j] = dp[i - 1][j];

                // 如果此时 nums[i] 恰巧为 j
                // 那么对于 dp[i][j] 来说,nums 的前 i 个元素可以组成和为 j 的结果
                if (nums[i] == j) {
                    // 所以 dp[i][j] 为 true
                    dp[i][j] = true;
                    // 同时继续
                    continue;
                }

                // 如果发现 nums[i] 小于 j
                if (nums[i] < j) {
                    // dp[i][j] 表示 nums 的前 i 个元素能否可以组成和为 j 的结果
                    // dp[i - 1][j] 表示 nums 的前 i - 1 个元素能否可以组成和为 j 的结果
                    // dp[i - 1][j - nums[i]] 表示 nums 的前 i - 1 个元素能否可以组成和为 j - nums[i] 的结果
                    // 那么 dp[i][j] 的结果要为 true
                    // 1、nums 的前 i - 1 个元素可以组成和为 j 
                    // 2、nums 的前 i - 1 个元素可以组成和为 j - nums[i]
                    // 对于 1 来说,不用使用 nums[i] 就组成了 j
                    // 对于 2 来说,前 i - 1 个元素可以组成和为 j - nums[i],那么加上此时的值 nums[i],也组成了 j
                    dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i]];
                }
            }
        }

        // dp[n - 1][target ] 表示 nums 的前 n - 1 个元素能否可以组成和为 target 的结果
        // 返回这个结果
        return dp[n - 1][target];
    }
};

# 3、Python 代码

class Solution:
    def canPartition(self, nums: List[int]) -> bool:
        # 使用 total 来保存数组中全部元素的和
        total = sum(nums)

        # 如果发现 sum 为奇数,那么必然无法拆分为两个相等的整数
        if total & 1:
            return False

        # 获取数组的长度
        n = len(nums)

        # 获取数组中全部元素之后的一半
        # 接下来需要在数组 nums 中寻找一些数字去拼凑为 target
        # 如果能找到一些数字之和为 target,那么剩下的数字之和也是 target
        target = total // 2

        # dp[i][j] 表示 nums 的前 i 个元素能否可以组成和为 j 的结果
        # dp[0][0] 表示 nums 的前 0 个元素能否可以组成和为 0 的结果
        # dp[2][6] 表示 nums 的前 2 个元素能否可以组成和为 6 的结果
        # dp[n - 1][target ] 表示 nums 的前 n - 1 个元素能否可以组成和为 target 的结果
        # i 的值从 0 到 n - 1
        # j 的值从 0 到 target
        dp = [[False] * (target + 1) for _ in range(n)]

        # 先初始化  dp[0][nums[0]]
        # 如果 nums 的第 0 个元素 nums[0] 小于 target
        if  nums[0] <= target : 
            # 那么 nums 的前 0 个元素能否可以组成和为 nums[0] 的结果是 true
            # 因为 nums 的前 0 个元素就是 nums[0]
            dp[0][nums[0]] = True
        

        # 接下来开始往 dp 数组中填充结果
        # i 的值从 1 到 n - 1
        for i in range( 1 , n ):
            # j 的值从 0 到 target
            for j in range( 0 , target + 1 ):
                # dp[i][j] 表示 nums 的前 i 个元素能否可以组成和为 j 的结果
                # dp[i - 1][j] 表示 nums 的前 i - 1 个元素能否可以组成和为 j 的结果
                # 对于 dp[i][j] 来说,如果 dp[i - 1][j] 为 true,那么 dp[i][j] 必然也为 true
                # 所以,先初始化 dp[i][j] 的值为 dp[i - 1][j] 的值
                # 再通过后面的代码修改 dp[i][j] 中还为 false 的值 
                dp[i][j] = dp[i - 1][j]

                # 如果此时 nums[i] 恰巧为 j
                # 那么对于 dp[i][j] 来说,nums 的前 i 个元素可以组成和为 j 的结果
                if nums[i] == j  : 
                    # 所以 dp[i][j] 为 true
                    dp[i][j] = True
                    # 同时继续
                    continue
                

                # 如果发现 nums[i] 小于 j
                if nums[i] < j : 
                    # dp[i][j] 表示 nums 的前 i 个元素能否可以组成和为 j 的结果
                    # dp[i - 1][j] 表示 nums 的前 i - 1 个元素能否可以组成和为 j 的结果
                    # dp[i - 1][j - nums[i]] 表示 nums 的前 i - 1 个元素能否可以组成和为 j - nums[i] 的结果
                    # 那么 dp[i][j] 的结果要为 true
                    # 1、nums 的前 i - 1 个元素可以组成和为 j 
                    # 2、nums 的前 i - 1 个元素可以组成和为 j - nums[i]
                    # 对于 1 来说,不用使用 nums[i] 就组成了 j
                    # 对于 2 来说,前 i - 1 个元素可以组成和为 j - nums[i],那么加上此时的值 nums[i],也组成了 j
                    dp[i][j] = dp[i - 1][j] | dp[i - 1][j - nums[i]]


        # dp[n - 1][target ] 表示 nums 的前 n - 1 个元素能否可以组成和为 target 的结果
        # 返回这个结果
        return dp[n - 1][target]